package com.jctrip.hotel.source.service.wehotel.impl;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jctrip.hotel.common.util.BeanUtil;
import com.jctrip.hotel.domain.dao.HtOrderSourceRecordDao;
import com.jctrip.hotel.domain.dao.HtSourceInfoDao;
import com.jctrip.hotel.domain.entity.HtSourceInfo;
import com.jctrip.hotel.source.model.wehotel.req.*;
import com.jctrip.hotel.source.model.wehotel.resq.*;
import com.jctrip.hotel.source.service.HotelCommonTaskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.jctrip.hotel.common.constant.RedisKeyConstant;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.common.util.RedisUtil;
import com.jctrip.hotel.source.client.WeHotelClient;
import com.jctrip.hotel.source.model.OrderDataResult;
import com.jctrip.hotel.source.model.PriceCheckParam;
import com.jctrip.hotel.source.service.wehotel.WeHotelService;
import com.jctrip.hotel.source.util.WeHotelUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author henry
 * @Date 2023/6/6 22:38
 * @Description
 */
@Slf4j
@Service
public class WeHotelServiceImpl implements WeHotelService {

    private final static Integer QPS_COUNT = 10;
    private final static Long TIME_OUT = 1000L;
    @Resource
    private WeHotelClient weHotelClient;
    @Resource
    private HtSourceInfoDao htSourceInfoDao;
    @Resource
    private HtOrderSourceRecordDao htOrderSourceRecordDao;
    @Resource
    private RedisUtil redisUtil;
    @Autowired
    private HotelCommonTaskService hotelCommonTaskService;

    @Override
    public String parseHotelId(String innId) {
        if (StringUtils.isBlank(innId)) {
            return null;
        }
        if (innId.startsWith(SourceEnum.WEHOTEL.getCode())) {
            return innId.substring(SourceEnum.WEHOTEL.getCode().length());
        }
        return innId;
    }

    @Override
    public String parseInnId(String hotelId) {
        if (null == hotelId) {
            return null;
        }
        return SourceEnum.WEHOTEL.getCode() + hotelId;
    }

    @Override
    public GetBrandListResult getBrandList(String appId, String key, GetBrandList getBrandList) {
        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);
        WeHotelResult<GetBrandListResult> brandList = weHotelClient.getBrandList(appId, timestamp, sign, getBrandList);
        if (null == brandList) {
            return null;
        }
        if (0 != brandList.getMsgCode()) {
            throw new RuntimeException(brandList.getMessage());
        }
        return brandList.getResult();
    }

    @Override
    public GetHotelIdsResult getHotelIdPage(String appId, String key, GetHotelIds getHotelIds) {

        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);
        log.info("wehotelParam = {}", JsonUtil.toString(getHotelIds));
        WeHotelResult<GetHotelIdsResult> getHotelIdsResultWeHotelResult =
                weHotelClient.getHotelIds(appId, timestamp, sign, getHotelIds);
        log.info("wehotelResult = {}", JsonUtil.toString(getHotelIdsResultWeHotelResult));
        if (null == getHotelIdsResultWeHotelResult) {
            return null;
        }

        if (0 != getHotelIdsResultWeHotelResult.getMsgCode()) {
            throw new RuntimeException(getHotelIdsResultWeHotelResult.getMessage());
        }

        return getHotelIdsResultWeHotelResult.getResult();
    }

    @Override
    public HotelInfoData getHotelInfo(String appId, String key, GetHotelInfo getHotelInfo) {
        if (StringUtils.isBlank(getHotelInfo.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }


        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);

        WeHotelResult<HotelInfoData> hotelInfoDataWeHotelResult =
                weHotelClient.getHotelInfo(appId, timestamp, sign, getHotelInfo);

        if (null == hotelInfoDataWeHotelResult) {
            return null;
        }

        if (0 != hotelInfoDataWeHotelResult.getMsgCode()) {
            throw new RuntimeException(hotelInfoDataWeHotelResult.getMessage());
        }

        HotelInfoData hotelInfoData = hotelInfoDataWeHotelResult.getResult();

        return hotelInfoData;
    }

    @Override
    public GetHotelImageResult getHotelImage(String appId, String key, GetHotelImage getHotelImage) {
        if (StringUtils.isBlank(getHotelImage.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }

        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);

        WeHotelResult<GetHotelImageResult> hotelImageResultWeHotelResult =
                weHotelClient.getHotelImage(appId, timestamp, sign, getHotelImage);

        if (null == hotelImageResultWeHotelResult) {
            return null;
        }

        if (0 != hotelImageResultWeHotelResult.getMsgCode()) {
            throw new RuntimeException(hotelImageResultWeHotelResult.getMessage());
        }

        GetHotelImageResult hotelImageResult = hotelImageResultWeHotelResult.getResult();

        return hotelImageResult;
    }

    @Override
    public GetHotelRoomTypeResult getHotelRoomTypeResult(String appId, String key, GetHotelRoomType getHotelRoomType) {
        if (StringUtils.isBlank(getHotelRoomType.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }

        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);

        WeHotelResult<GetHotelRoomTypeResult> getHotelRoomTypeResultWeHotelResult =
                weHotelClient.getHotelRoomType(appId, timestamp, sign, getHotelRoomType);

        if (null == getHotelRoomTypeResultWeHotelResult) {
            return null;
        }

        if (0 != getHotelRoomTypeResultWeHotelResult.getMsgCode()) {
            throw new RuntimeException(getHotelRoomTypeResultWeHotelResult.getMessage());
        }

        GetHotelRoomTypeResult getHotelRoomTypeResult = getHotelRoomTypeResultWeHotelResult.getResult();

        return getHotelRoomTypeResult;
    }

    @Override
    public GetHotelRoomStatusResult getHotelRoomStatusResult(String appId, String key,
                                                             GetHotelRoomStatus getHotelRoomStatus) {

        if (StringUtils.isBlank(getHotelRoomStatus.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }

        if (StringUtils.isBlank(getHotelRoomStatus.getEndOfDay())) {
            throw new RuntimeException("营业日期为空");
        }
        WeHotelResult<GetHotelRoomStatusResult> getHotelRoomStatusResultWeHotelResult = null;
        // 一直重试，直到我拿到机会进行请求
        while (true) {
            if (StringUtils.isNotBlank(
                    redisUtil.acquireToken(RedisKeyConstant.HOTEL_MAIN_WEHOTEL_LIMIT_LOCK,
                            QPS_COUNT,
                            TIME_OUT))) {
                long timestamp = System.currentTimeMillis();
                String sign = WeHotelUtil.getSign(appId, key, timestamp);
                getHotelRoomStatusResultWeHotelResult =
                        weHotelClient.getHotelRoomStatus(appId, timestamp, sign, getHotelRoomStatus);
                break;
            }
        }
        if (null == getHotelRoomStatusResultWeHotelResult) {
            return null;
        }

        if (0 != getHotelRoomStatusResultWeHotelResult.getMsgCode()) {
            throw new RuntimeException(getHotelRoomStatusResultWeHotelResult.getMessage());
        }

        return getHotelRoomStatusResultWeHotelResult.getResult();
    }

    @Override
    public GetHotelRoomStatusResult getHotelRealRoomStatusResult(String appId, String key,
                                                                 GetHotelRealRoomStatus getHotelRealRoomStatus) {
        if (StringUtils.isBlank(getHotelRealRoomStatus.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }

        if (StringUtils.isBlank(getHotelRealRoomStatus.getRoomTypeCode())) {
            throw new RuntimeException("房型编号为空");
        }

        if (StringUtils.isBlank(getHotelRealRoomStatus.getEndOfDay())) {
            throw new RuntimeException("营业日期为空");
        }

        if (null == getHotelRealRoomStatus.getDays()) {
            throw new RuntimeException("查询天数为空");
        }
        if (StringUtils.isBlank(getHotelRealRoomStatus.getProductCode())) {
            throw new RuntimeException("价格码为空");
        }

        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);

        WeHotelResult<GetHotelRoomStatusResult> getHotelRoomStatusResultWeHotelResult =
                weHotelClient.getHotelRealRoomStatus(appId, timestamp, sign, getHotelRealRoomStatus);

        if (null == getHotelRoomStatusResultWeHotelResult) {
            return null;
        }

        if (0 != getHotelRoomStatusResultWeHotelResult.getMsgCode()) {
            throw new RuntimeException(getHotelRoomStatusResultWeHotelResult.getMessage());
        }

        return getHotelRoomStatusResultWeHotelResult.getResult();
    }

    @Override
    public OrderDataResult postOrder(String appId, String key, PostOrder postOrder, PriceCheckParam priceCheck) {
        if (StringUtils.isBlank(postOrder.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }

        if (StringUtils.isBlank(postOrder.getRoomTypeId())) {
            throw new RuntimeException("房型唯一编号为空");
        }

        if (StringUtils.isBlank(postOrder.getProductCode())) {
            throw new RuntimeException("商品唯一编号为空");
        }

        if (null == postOrder.getRoomCount()) {
            throw new RuntimeException("房间数量为空");
        }

        if (StringUtils.isBlank(postOrder.getDtArrorig())) {
            throw new RuntimeException("入住日为空");
        }

        if (StringUtils.isBlank(postOrder.getDtDeporig())) {
            throw new RuntimeException("离店日为空");
        }

        if (null == postOrder.getPayType()) {
            throw new RuntimeException("支付方式为空");
        }

        if (StringUtils.isBlank(postOrder.getExternalId())) {
            throw new RuntimeException("第三方渠道订单编号为空");
        }

        if (CollectionUtils.isEmpty(postOrder.getPassengers())) {
            throw new RuntimeException("入住人信息为空");
        }


        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);
        if (StringUtils.isBlank(postOrder.getBookName())) {
            postOrder.setBookName(postOrder.getPassengers().get(0).getGuestName());
            postOrder.setBookMobile(postOrder.getPassengers().get(0).getGuestMobile());
        }
        GetHotelRoomStatusResult hotelRealRoomStatusResult = null;
        List<String> productCodeList = JsonUtil.toList(String.class, postOrder.getProductCode());

        AtomicReference<BigDecimal> originAdvanceRateAtomic = new AtomicReference<>(new BigDecimal(0));
        AtomicReference<BigDecimal> raisedTotalPrice = new AtomicReference<>(new BigDecimal(0));
        for (String productCode : Optional.ofNullable(productCodeList)
                .orElseThrow(() -> new RuntimeException("房价码不能为空"))) {
            try {
                postOrder.setProductCode(productCode);
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                // 查询房态，获取订单原始金额进行替换
                GetHotelRealRoomStatus getHotelRealRoomStatus = new GetHotelRealRoomStatus();
                getHotelRealRoomStatus.setInnId(postOrder.getInnId());
                getHotelRealRoomStatus.setRoomTypeCode(postOrder.getRoomTypeId());
                getHotelRealRoomStatus.setEndOfDay(postOrder.getDtArrorig());
                getHotelRealRoomStatus.setProductCode(postOrder.getProductCode());
                getHotelRealRoomStatus.setDays(Integer.valueOf(String.valueOf(
                        (simpleDateFormat.parse(postOrder.getDtDeporig()).getTime() - simpleDateFormat.parse(
                                postOrder.getDtArrorig()).getTime()) / (1000 * 3600 * 24))));
                hotelRealRoomStatusResult =
                        getHotelRealRoomStatusResult(appId, key, getHotelRealRoomStatus);

                if (hotelRealRoomStatusResult == null) {
                    continue;
                }

                Optional.ofNullable(hotelRealRoomStatusResult.getRoomTypeList()).orElse(new ArrayList<>())
                        .forEach(roomTypeInfo -> {
                            roomTypeInfo.getProductList().forEach(productData -> {
                                originAdvanceRateAtomic.set(
                                        originAdvanceRateAtomic.get().add(productData.getAdvanceRate()));
                                raisedTotalPrice.set(
                                        raisedTotalPrice.get()
                                                .add(priceCheck.getRaiseFunc().apply(productData.getAdvanceRate())));
                            });
                        });
                if (raisedTotalPrice.get().compareTo(BigDecimal.ZERO) == 1
                        && originAdvanceRateAtomic.get().compareTo(BigDecimal.ZERO) == 1) {
                    break;
                }
            } catch (Exception e) {
                log.error("getHotelRealRoomStatusResult error postOrder {}", JsonUtil.toString(postOrder), e);
            }
        }

        if (hotelRealRoomStatusResult == null) {
            hotelCommonTaskService.insertOrderErrorLog(postOrder.getExternalId(), JsonUtil.toString(postOrder), "酒店房态信息为空");
            throw new RuntimeException("酒店房态信息为空");
        }
        // todo 临时价格不还原
//        if (priceCheck.getTotalRate().compareTo(raisedTotalPrice.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount()))
//                .subtract(priceCheck.getTolerancePrice())) < 0) {
//            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}",
//                    priceCheck.getTotalRate(), raisedTotalPrice, priceCheck.getTolerancePrice());
//            throw new RuntimeException("订单销售价不能低于采购总价");
//        }
        if (priceCheck.getTotalRate().compareTo(originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount()))
                .subtract(priceCheck.getTolerancePrice())) < 0) {
            String error = String.format("订单销售价不能低于采购总价，订单销售价=%s，采购总价=%s, 容差金额=%s, 目前差额=%s",
                    priceCheck.getTotalRate(),
                    originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount())),
                    priceCheck.getTolerancePrice(),
                    originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount())).subtract(priceCheck.getTotalRate()));
            hotelCommonTaskService.insertOrderErrorLog(postOrder.getExternalId(), JsonUtil.toString(postOrder), error);
            log.error("订单销售价不能低于采购总价，订单销售价={}，采购总价={}, 容差金额={}, 目前差额={}",
                    priceCheck.getTotalRate(), originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount())),
                    originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount())).subtract(priceCheck.getTotalRate()));
            throw new RuntimeException("订单销售价不能低于采购总价");
        }
        postOrder.setTotalRate(originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount())).doubleValue());

        WeHotelResult<OrderData> orderDataWeHotelResult = weHotelClient.postOrder(appId, timestamp, sign, postOrder);

        if (null == orderDataWeHotelResult) {
            return new OrderDataResult();
        }

        if (0 != orderDataWeHotelResult.getMsgCode()) {
            hotelCommonTaskService.insertOrderErrorLog(postOrder.getExternalId(), JsonUtil.toString(postOrder), orderDataWeHotelResult.getMessage());
            throw new RuntimeException(orderDataWeHotelResult.getMessage());
        }

        OrderData orderData = orderDataWeHotelResult.getResult();
        return OrderDataResult.builder().orderCode(orderData.getOrderCode()).externalId(orderData.getExternalId())
                .orderState(orderData.getOrderState()).totalRate(originAdvanceRateAtomic.get().multiply(BigDecimal.valueOf(postOrder.getRoomCount())))
                .requestJsonStr(JsonUtil.toString(postOrder)).responseJsonStr(JsonUtil.toString(orderDataWeHotelResult))
                .build();
    }

    @Override
    public OrderData postOrder2C(String appId, String key, PostOrder postOrder) {
        if (StringUtils.isBlank(postOrder.getInnId())) {
            throw new RuntimeException("酒店唯一编号为空");
        }

        if (StringUtils.isBlank(postOrder.getRoomTypeId())) {
            throw new RuntimeException("房型唯一编号为空");
        }

        if (StringUtils.isBlank(postOrder.getProductCode())) {
            throw new RuntimeException("商品唯一编号为空");
        }

        if (null == postOrder.getRoomCount()) {
            throw new RuntimeException("房间数量为空");
        }

        if (StringUtils.isBlank(postOrder.getDtArrorig())) {
            throw new RuntimeException("入住日为空");
        }

        if (StringUtils.isBlank(postOrder.getDtDeporig())) {
            throw new RuntimeException("离店日为空");
        }

        if (null == postOrder.getPayType()) {
            throw new RuntimeException("支付方式为空");
        }

        if (StringUtils.isBlank(postOrder.getExternalId())) {
            throw new RuntimeException("第三方渠道订单编号为空");
        }

        if (CollectionUtils.isEmpty(postOrder.getPassengers())) {
            throw new RuntimeException("入住人信息为空");
        }

        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);
        if (StringUtils.isBlank(postOrder.getBookName())) {
            postOrder.setBookName(postOrder.getPassengers().get(0).getGuestName());
            postOrder.setBookMobile(postOrder.getPassengers().get(0).getGuestMobile());
        }

        WeHotelResult<OrderData> orderDataWeHotelResult = weHotelClient.postOrder(appId, timestamp, sign, postOrder);

        if (null == orderDataWeHotelResult) {
            return new OrderData();
        }

        if (0 != orderDataWeHotelResult.getMsgCode()) {
            hotelCommonTaskService.insertOrderErrorLog(postOrder.getExternalId(), JsonUtil.toString(postOrder), orderDataWeHotelResult.getMessage());
            throw new RuntimeException(orderDataWeHotelResult.getMessage());
        }

        return orderDataWeHotelResult.getResult();
    }

    @Override
    public OrderData queryOrder(String appId, String key, QueryOrder queryOrder) {
        if (StringUtils.isBlank(queryOrder.getJsonOrderID()) && StringUtils.isBlank(queryOrder.getExternalId())) {
            throw new RuntimeException("订单编号为空");
        }
        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);
        // 一直重试，直到我拿到机会进行请求
        while (true) {
            if (StringUtils.isNotBlank(
                    redisUtil.acquireToken(RedisKeyConstant.HOTEL_MAIN_WEHOTEL_LIMIT_LOCK,
                            QPS_COUNT,
                            TIME_OUT))) {
                WeHotelResult<QueryOrderResp> orderDataWeHotelResult = weHotelClient.queryOrder(appId, timestamp, sign, queryOrder);
                if (null == orderDataWeHotelResult) {
                    return null;
                }

                if (0 != orderDataWeHotelResult.getMsgCode()) {
                    throw new RuntimeException(orderDataWeHotelResult.getMessage());
                }
                OrderData orderData = new OrderData();
                BeanUtil.copyProperties(orderDataWeHotelResult.getResult(), orderData);
//                orderData = orderDataWeHotelResult.getResult();
                return orderData;
            }
        }
    }


    @Override
    public void cancelOrder(String appId, String key, CancelOrder cancelOrder) {
        if (StringUtils.isBlank(cancelOrder.getJsonOrderID())) {
            throw new RuntimeException("订单编号为空");
        }

        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);

        WeHotelResult weHotelResult = weHotelClient.cancelOrder(appId, timestamp, sign, cancelOrder);

        if (0 != weHotelResult.getMsgCode()) {
            throw new RuntimeException(weHotelResult.getMessage());
        }
    }

    /**
     * 查询订单所有信息
     *
     * @param sourceId
     * @param queryOrder
     * @return
     */
    @Override
    public QueryOrderResp queryOrderAll(String sourceId, QueryOrder queryOrder) {
        if (StringUtils.isBlank(queryOrder.getJsonOrderID()) && StringUtils.isBlank(queryOrder.getExternalId())) {
            throw new RuntimeException("订单编号为空");
        }
        HtSourceInfo htSourceInfo =
                htSourceInfoDao.selectOne(new LambdaQueryWrapper<HtSourceInfo>().eq(HtSourceInfo::getSourceId,
                        sourceId));
        if (htSourceInfo == null) {
            throw new RuntimeException("供应商账户为空");
        }
        String appId = htSourceInfo.getSourceAppId();
        String key = htSourceInfo.getSourceSecret();

        QueryOrderResp queryOrderResp = null;
        long timestamp = System.currentTimeMillis();
        String sign = WeHotelUtil.getSign(appId, key, timestamp);
        // 不断请求，直到请求成功
        while (true) {
            //接口限流请求成功，进行req请求
            if (StringUtils.isNotBlank(redisUtil.acquireToken(RedisKeyConstant.HOTEL_MAIN_WEHOTEL_LIMIT_LOCK,
                    QPS_COUNT,
                    TIME_OUT))) {
                WeHotelResult<QueryOrderResp> resp = weHotelClient.queryOrder(appId, timestamp, sign, queryOrder);
                if (null == resp) {
                    return queryOrderResp;
                }

                if (0 != resp.getMsgCode()) {
                    throw new RuntimeException(resp.getMessage());
                }

                queryOrderResp = resp.getResult();
                return queryOrderResp;
            }
        }
    }
}
